/*
 * textcodec.c
 *
 *  Created on: 2022年12月7日
 *      Author: lakun@qq.com
 */


#include "textcodec.h"

/**
 * 十六进制字符转十六进制数，例：'A' -> 0xA
 * @param  chr 要转换的十六进制字符
 * @return     转换后的值
 */
uint8_t chr2hex(uint8_t chr)
{
	if (chr >= '0' && chr <= '9')return chr - '0';
	if (chr >= 'A' && chr <= 'F')return (chr - 'A' + 10);
	if (chr >= 'a' && chr <= 'f')return (chr - 'a' + 10);
	return 0;
}

/**
 * 十六进制数转为字符串形式，例：0x0A->'A'
 * @param  hex 要转换的数，范围0~F
 * @return     转换后的值
 */
uint8_t hex2chr(uint8_t hex)
{
	if (hex <= 9)return hex + '0';
	if (hex >= 10 && hex <= 15)return (hex - 10 + 'A');
	return '0';
}

void HexToStr(const uint8_t* from, uint32_t fromSize, char* to, uint32_t* toSize)
{
	uint32_t size = 0;
	while(fromSize != 0)
	{
		*to++ = hex2chr((*from >> 4) & 0X0F);
		*to++ = hex2chr((*from ) & 0X0F);
		size += 2;
		from++;
		fromSize--;
	}
	*to = 0;   // 添加结束符
	*toSize = size;
}

void StrToHex(const char* from, uint32_t fromSize, uint8_t* to, uint32_t* toSize)
{
	uint32_t size = 0;
	while (fromSize > 1)
	{
		*to++  = (chr2hex(from[0]) << 4) | chr2hex(from[1]);
		size++;
		from     += 2;
		fromSize -= 2;
	}
	*toSize = size;
}

#if (TEXTCODEC_ENABLE == 1)

static uint16_t ff_convert (uint16_t src, uint32_t	dir);

void GBKToUTF8(const uint8_t* from, uint32_t fromSize, uint8_t* to, uint32_t* toSize)
{
    uint32_t unicode;
	uint32_t utfcode;
	uint32_t size = 0;
	while(fromSize != 0)
	{
		if(*from < 0X80)             // ASCII
		{
			unicode = *from++;
			fromSize--;
		}else                        // GBK
		{
			unicode = (from[0] << 8) | from[1];  // 一个GBK字符占用两个字节(大端模式)
			unicode = ff_convert(unicode, 1);    // to unicode
			from += 2;
			fromSize -= 2;
		}

		if (unicode < 0X80U)         // ASCII
		{
			*to = unicode;
			to++;
			size++;
		}
		else                         // NOT ASCII
		{
			if (unicode >= 0X80U && unicode <= 0X7FFU)
			{
				utfcode = 0XC080U | (unicode & 0X3FU) | (((unicode >> 6) & 0X1FU) << 8);
				to[1] = utfcode & 0XFF;
				to[0] = (utfcode >> 8) & 0XFF;
				to   += 2;
				size += 2;
			}
			else if (unicode >= 0X800U && unicode <= 0XFFFFU)
			{
				utfcode = 0XE08080U | (unicode & 0X3FU) | (((unicode >> 6) & 0X3FU) << 8) | (((unicode >> 12) & 0XFU) << 16);
				to[2] = utfcode & 0XFF;
				to[1] = (utfcode >>  8) & 0XFF;
				to[0] = (utfcode >> 16) & 0XFF;
				to   += 3;
				size += 3;
			}
			else if (unicode >= 0X10000U && unicode <= 0X10FFFFU)
			{
				utfcode = 0XF0808080U | ((unicode & 0X3FU)) | (((unicode >> 6) & 0X3FU) << 8) | (((unicode >> 12) & 0X3FU) << 16) | (((unicode >> 18) & 0X7U) << 24);
				to[3] = utfcode & 0XFF;
				to[2] = (utfcode >>  8) & 0XFF;
				to[1] = (utfcode >> 16) & 0XFF;
				to[0] = (utfcode >> 24) & 0XFF;
				to   += 4;
				size += 4;
			}
			else
			{
				break; // ERROR: not support
			}
		}
	}
	*toSize = size;
}

void UTF8ToGBK(const uint8_t* from, uint32_t fromSize, uint8_t* to, uint32_t* toSize)
{
	uint32_t unicode;
	uint32_t utfcode;
	uint32_t size = 0;
	while(fromSize != 0)
	{
		if(*from < 0X80)        // ASCII
		{
			utfcode = *from;
			from++;
			fromSize--;
		}else                   // UTF8
		{
			utfcode = (from[0] << 16) | (from[1] << 8) | from[2];  // UTF8用三个字节表示一个中文汉字(大端模式)
			from += 3;
			fromSize -= 3;
		}

		if (utfcode < 0X80U)
		{
			unicode = utfcode;
		} else if (utfcode >= 0X0000C080U && utfcode <= 0X0000DFBFU)
		{
			unicode = (((utfcode >> 8) & 0X1FU) << 6) | (utfcode & 0X3FU);
		} else if (utfcode >= 0X00E08080U && utfcode <= 0X00EFBFBFU)
		{
			unicode = (((utfcode >> 16) & 0XFU) << 12) | (((utfcode >> 8) & 0X3FU) << 6) | (utfcode & 0X3FU);
		} else if (utfcode >= 0XF0808080U && utfcode <= 0XF7BFBFBFU)
		{
			unicode = (((utfcode >> 24) & 0X7U) << 18) | (((utfcode >> 16) & 0X3FU) << 12) | (((utfcode >> 8) & 0X3FU) << 6) | (utfcode & 0X3FU);
		}else
		{
			break;  // ERROR: not support
		}

		if(unicode < 0X80)        // ASCII
		{
			*to++ = unicode;
			size++;
		}else                     // NOT ASCII
		{
			unicode = ff_convert(unicode, 0);  // UNICODE TO GBK
			to[0] = (unicode >> 8) & 0XFF;     // 大端存储
			to[1] = unicode & 0XFF;            // 大端存储
			to   += 2;
			size += 2;
		}
	}
	*toSize = size;
}

void GBKToUnicode(const uint8_t* from, uint32_t fromSize, uint8_t* to, uint32_t* toSize)
{
	uint32_t size = 0;
	uint16_t unicode;
	while(fromSize != 0)
	{
		if(*from < 0X80)             // ASCII
		{
			unicode = *from++;
			fromSize--;
		}else                        // GBK
		{
			unicode = (from[0] << 8) | from[1];       // GBK为大端模式
			unicode = ff_convert(unicode, 1);         // to unicode
			from += 2;
			fromSize -= 2;
		}
		to[0] = unicode & 0XFF;                       // 小端模式存储UNICODE码
		to[1] = (unicode >> 8) & 0XFF;                // 小端模式存储UNICODE码
		to   += 2;
		size += 2;
	}
	*toSize = size;
}

void UnicodeToGBK(const uint8_t* from, uint32_t fromSize, uint8_t* to, uint32_t* toSize)
{
	uint32_t size = 0;
	uint16_t unicode;
	while(fromSize != 0)
	{
		unicode = (from[1] << 8) | from[0];    // unicode码为小端模式
		if(unicode < 0X80)                     // ASCII
		{
			*to++ = unicode;
			size++;
		}else                                     // NOT ASCII
		{
			unicode = ff_convert(unicode, 0);     // TO GBK
			to[0] = (unicode >> 8) & 0XFF;        // 大端模式存储GBK码
			to[1] = unicode & 0XFF;               // 大端模式存储GBK码
			size += 2;
			to   += 2;
		}
		from     += 2;
		fromSize -= 2;
	}
	*toSize = size;
}

void UTF8ToUnicode(const uint8_t* from, uint32_t fromSize, uint8_t* to, uint32_t* toSize)
{
	uint32_t unicode;
	uint32_t utfcode;
	uint32_t size = 0;
	while(fromSize != 0)
	{
		if(*from < 0X80)          // ASCII
		{
			utfcode = *from;
			from++;
			fromSize--;
		}else                     // NOT ASCII
		{
			utfcode = (from[0] << 16) | (from[1] << 8) | from[2];  // UTF8用三个字节表示一个中文汉字(大端模式)
			from += 3;
			fromSize -= 3;
		}

		if (utfcode < 0X80U)      // ASCII
		{
			unicode = utfcode;
		} else                    // NOT ASCII
		{
			if (utfcode >= 0X0000C080U && utfcode <= 0X0000DFBFU)
			{
				unicode = (((utfcode >> 8) & 0X1FU) << 6) | (utfcode & 0X3FU);
			} else if (utfcode >= 0X00E08080U && utfcode <= 0X00EFBFBFU)
			{
				unicode = (((utfcode >> 16) & 0XFU) << 12) | (((utfcode >> 8) & 0X3FU) << 6) | (utfcode & 0X3FU);
			} else if (utfcode >= 0XF0808080U && utfcode <= 0XF7BFBFBFU)
			{
				unicode = (((utfcode >> 24) & 0X7U) << 18) | (((utfcode >> 16) & 0X3FU) << 12) | (((utfcode >> 8) & 0X3FU) << 6) | (utfcode & 0X3FU);
			}else
			{
				break;   // ERROR: not support
			}
		}

		to[0] = unicode & 0XFF;            // 小端存储UNICODE
		to[1] = (unicode >> 8) & 0XFF;     // 小端存储UNICODE
		to += 2;
		size += 2;
	}
	*toSize = size;
}

void UnicodeToUTF8(const uint8_t* from, uint32_t fromSize, uint8_t* to, uint32_t* toSize)
{
	uint32_t unicode;
	uint32_t utfcode;
	uint32_t size = 0;
	while(fromSize != 0)
	{
		unicode = (from[1] << 8) | from[0];    // unicode码为小端模式
		from += 2;
		fromSize -= 2;

		if (unicode < 0X80U)   // ASCII
		{
			*to = unicode;
			to++;
			size++;
		}else                  // NOT ASCII
		{
			if (unicode >= 0X80U && unicode <= 0X7FFU)
			{
				utfcode = 0XC080U | (unicode & 0X3FU) | (((unicode >> 6) & 0X1FU) << 8);
				to[1] = utfcode & 0XFF;
				to[0] = (utfcode >> 8) & 0XFF;
				to   += 2;
				size += 2;
			}
			else if (unicode >= 0X800U && unicode <= 0XFFFFU)
			{
				utfcode = 0XE08080U | (unicode & 0X3FU) | (((unicode >> 6) & 0X3FU) << 8) | (((unicode >> 12) & 0XFU) << 16);
				to[2] = utfcode & 0XFF;
				to[1] = (utfcode >>  8) & 0XFF;
				to[0] = (utfcode >> 16) & 0XFF;
				to   += 3;
				size += 3;
			}
			else if (unicode >= 0X10000U && unicode <= 0X10FFFFU)
			{
				utfcode = 0XF0808080U | ((unicode & 0X3FU)) | (((unicode >> 6) & 0X3FU) << 8) | (((unicode >> 12) & 0X3FU) << 16) | (((unicode >> 18) & 0X7U) << 24);
				to[3] = utfcode & 0XFF;
				to[2] = (utfcode >>  8) & 0XFF;
				to[1] = (utfcode >> 16) & 0XFF;
				to[0] = (utfcode >> 24) & 0XFF;
				to   += 4;
				size += 4;
			}
			else
			{
				break;  // ERROR: not support
			}
		}
	}
	*toSize = size;
}

/**
 * UNICODE和GBK互转函数
 * @param  src 待转换的值
 * @param  dir 0：unicode转gbk  1：gbk转unicode
 * @return     转换后的值
 */
uint16_t ff_convert (uint16_t src, uint32_t	dir)
{
	static const uint16_t uni2oem[] =
	{
		/*  Unicode - OEM,  Unicode - OEM,  Unicode - OEM,  Unicode - OEM */
		#include "textcodecTab.c"
	};

	const uint16_t *p;
	uint16_t c;
	int i, n, li, hi;

	if (src < 0x80)  	/* ASCII */
	{
		c = src;
	}
	else
	{
		if (dir)  		/* OEMCP to unicode */
		{
			p = oem2uni;
			hi = (sizeof(oem2uni) >> 2) - 1;
		}
		else  		/* Unicode to OEMCP */
		{
			p = uni2oem;
			hi = (sizeof(uni2oem) >> 2) - 1;
		}
		li = 0;
		for (n = 16; n; n--)
		{
			i = li + (hi - li) / 2;
			if (src == p[i << 1]) break;
			if (src > p[i << 1])
				li = i;
			else
				hi = i;
		}
		c = n ? p[(i << 1) + 1] : 0;
	}

	return c;
}

#endif
